/*++
"This software program is available to you under a choice of one of two 
licenses.  You may choose to be licensed under either the GNU General Public 
License (GPL) Version 2, June 1991, available at 
http://www.fsf.org/copyleft/gpl.html, or the BSD License, the text of
which follows:

Copyright (c) 1996-2005, Intel Corporation. All rights reserved.

Redistribution and use in source and binary forms, with or without 
modification, are permitted provided that the following conditions are met:

Redistributions of source code must retain the above copyright notice, this 
list of conditions and the following disclaimer. 

Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or 
other materials provided with the distribution. 

Neither the name of the Intel Corporation ("Intel") nor the names of its 
contributors may be used to endorse or promote products derived from this
software without specific prior written permission. 

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
--*/

#include "xllp_gpio.h"
#include "xllp_mfp.h"
#include "xllp_i2c.h"
#include "xllp_micco_audio.h"
#include "xllp_mfp_plat.h"
#include "xllp_ssp_cpu.h"
#include "xllp_ssp_peri.h"
#include "xllp_ssp_plat.h"
#include "micco.h"
#include "windows.h"

#define	MICCO_MASTER_MODE

#define	MICCO_INPUT_PORT	16
#define   PCM_SAMPLERATE      16000

static	struct MiccoObject	g_Codec;
static       struct MiccoObject g_Codec_saved;

extern XLLP_STATUS_T XllpPmMiccoWrite(XLLP_I2C_T *i2cRegs, 
			XLLP_OST_T *ostRegs, 
			XLLP_UINT8_T reg, XLLP_UINT8_T val);
extern XLLP_STATUS_T XllpPmMiccoRead(XLLP_I2C_T *i2cRegs, 
			XLLP_OST_T *ostRegs, 
			XLLP_UINT8_T reg, XLLP_UINT8_T *pval);

XLLP_ACODEC_ERROR_T XllpSSPPortInit(XLLP_ACODEC_CONTEXT_T *pDeviceContext);

XLLP_UINT8_T MICCO_READ(XLLP_UINT8_T reg)
{
	XLLP_UINT8_T	Data;
	reg += XLLP_MICCO_AUDIO_REG_BASE;
	Data = 0xFF;
	XllpPmMiccoRead((XLLP_I2C_T*)g_Codec.I2CBase,
			(XLLP_OST_T*)g_Codec.OSTBase,
			reg, &Data);
	return Data;
}


void MICCO_WRITE(XLLP_UINT8_T reg)
{
	XllpPmMiccoWrite((XLLP_I2C_T*)g_Codec.I2CBase,
			(XLLP_OST_T*)g_Codec.OSTBase,
			(XLLP_UINT8_T)(XLLP_MICCO_AUDIO_REG_BASE+reg), 
			(XLLP_UINT8_T)g_Codec.Reg[reg]);
	return;
}

void MiccoReadCodec(struct MiccoObject* codec)
{
	XLLP_UINT8_T n;
	if(codec){
		for(n=0;n<=XLLP_MICCO_AUDIO_REGS_NUM;n++){
			codec->Reg[n] = MICCO_READ(n);
		}
	}
}

XLLP_ACODEC_ERROR_T MiccoCodecInit(void)
{
	XLLP_UINT8_T offset=0;
	XLLP_UINT8_T i;

    offset = XLLP_MICCO_MUX_MONO;
    g_Codec.Reg[offset] = 0x00; /* MONO */

    offset = XLLP_MICCO_MUX_BEAR;
    g_Codec.Reg[offset] = 0x01; /* BEAR */
 
    offset = XLLP_MICCO_MUX_LINE_OUT;
    g_Codec.Reg[offset] = 0x80; /* LINE OUT */

    offset = XLLP_MICCO_MUX_STEREO_CH1;
    g_Codec.Reg[offset] = 0x08;	/* STEREO_CH1 */

    offset = XLLP_MICCO_MUX_STEREO_CH2;
    g_Codec.Reg[offset] = 0x10;	/* STEREO_CH2 */

    offset = XLLP_MICCO_AUDIO_LINE_AMP;
    g_Codec.Reg[offset] = 0x25; //XLLP_MICCO_AUDIO_LINE_AMP_EN;	/* Enable the Line AMP */

    offset = XLLP_MICCO_HIFI_DAC_CONTROL; 
    g_Codec.Reg[offset] = XLLP_MICCO_HIFI_DAC_ON;

	/* Micco as I2S slave. Use I2S MSB normal mode */
    offset = XLLP_MICCO_I2S_CONTROL;
    g_Codec.Reg[offset] = XLLP_MICCO_I2S_MSB_JU_MODE;

    offset = XLLP_MICCO_TX_PGA;
    g_Codec.Reg[offset] = 0x0D;	/* 3 dB */
    
    offset = XLLP_MICCO_MIC_PGA;
    g_Codec.Reg[offset] = XLLP_MICCO_MIC_PGA_EXT_EN | XLLP_MICCO_MIC_PGA_INT_EN |
//        XLLP_MICCO_MIC_PGA_SELMIC_2 | XLLP_MICCO_MIC_PGA_AMP_EN |
        XLLP_MICCO_MIC_PGA_AMP_EN |
        0x0;	/* 30 dB*/	

    offset = XLLP_MICCO_TX_PGA_MUX;
    g_Codec.Reg[offset] = 0x00;

    offset = XLLP_MICCO_VCODEC_ADC_CONTROL;
    g_Codec.Reg[offset] = 0x00;//XLLP_MICCO_VCODEC_ADC_ON_EN;

	/* PCM_SDI normal operation, PCM_SDO enabled */
    offset = XLLP_MICCO_VCODEC_VDAC_CONTROL;
//    g_Codec.Reg[offset] = XLLP_MICCO_VDAC_ON | XLLP_MICCO_VDAC_HPF_BYPASS;
    g_Codec.Reg[offset] = XLLP_MICCO_VDAC_HPF_BYPASS;

    offset = XLLP_MICCO_SIDETONE;
    g_Codec.Reg[offset] = 0; 

    /* Enable AUX1,2. AUX1, 2 gain 0dB */
    offset = XLLP_MICCO_PGA_AUX1_2;
    g_Codec.Reg[offset] = 0;

    /* Enable AUX3. AUX3 gain 0 dB */	
    offset = XLLP_MICCO_PGA_AUX3;
    g_Codec.Reg[offset] = 0;
//    g_Codec.Reg[offset] = XLLP_MICCO_PGA_AUX3_EN;

    /* DAC1, 2, 3 gain 0dB */
    offset = XLLP_MICCO_PGA_DACS;
    g_Codec.Reg[offset] = 0x00;

    /*Soft start for MONO, BEAR LINE and STEREO is 61.5ms */
    offset = XLLP_MICCO_SOFT_START_RAMP;
    g_Codec.Reg[offset] = 0x00;

    for (i = 0; i < XLLP_MICCO_AUDIO_REGS_NUM; i++)
		MICCO_WRITE(i);

	return XLLP_ACODEC_SUCCESS;
}
 
XLLP_ACODEC_ERROR_T MiccoCodecEnableOutput(enum MiccoOutput out)
{
	XLLP_UINT8_T offset=0;
	switch(out){
	case CODEC_BEAR:
		offset = XLLP_MICCO_BEAR_VOL;
		if (!(g_Codec.Reg[offset] & XLLP_MICCO_BEAR_EN)) {
			g_Codec.Reg[offset] |=  XLLP_MICCO_BEAR_EN;
		}
		break;	

	case CODEC_MONO:
		offset = XLLP_MICCO_MONO_VOL;
		if (!(g_Codec.Reg[offset] & XLLP_MICCO_MONO_EN)) {
			g_Codec.Reg[offset] |= XLLP_MICCO_MONO_EN;
		}
		break;

	case CODEC_STEREO:
		offset = XLLP_MICCO_STEREO_AMPLITUDE_CH1;
		if (!(g_Codec.Reg[offset] & 	XLLP_MICCO_STEREO_EN)){
			g_Codec.Reg[offset] |= 
				XLLP_MICCO_STEREO_EN;
		}
		break;

	case CODEC_LINE_OUT:
		offset = XLLP_MICCO_AUDIO_LINE_AMP;
		if (!(g_Codec.Reg[offset] & 
				XLLP_MICCO_AUDIO_LINE_AMP_EN)) {
			g_Codec.Reg[offset] |= 
				XLLP_MICCO_AUDIO_LINE_AMP_EN;
		}
		break;

	case CODEC_ADC:
		offset = XLLP_MICCO_VCODEC_ADC_CONTROL;
		if (!(g_Codec.Reg[offset] & 
				XLLP_MICCO_VCODEC_ADC_ON_EN)) {
			g_Codec.Reg[offset] |= 
				XLLP_MICCO_VCODEC_ADC_ON_EN;
		}
		break;
	default:
		return XLLP_FALSE;
	}
       MICCO_WRITE(offset);

       return XLLP_ACODEC_SUCCESS;
}

XLLP_ACODEC_ERROR_T MiccoCodecDisableOutput(enum MiccoOutput out)
{
       XLLP_UINT_T offset;
	switch(out){
	case CODEC_BEAR:
		offset = XLLP_MICCO_BEAR_VOL;
		if (g_Codec.Reg[offset] & XLLP_MICCO_BEAR_EN) {
			g_Codec.Reg[offset] &=  ~XLLP_MICCO_BEAR_EN;
		}
		break;	

	case CODEC_MONO:
		offset = XLLP_MICCO_MONO_VOL;
		if (g_Codec.Reg[offset] & XLLP_MICCO_MONO_EN) {
			g_Codec.Reg[offset] &= ~XLLP_MICCO_MONO_EN;
		}
		break;

	case CODEC_STEREO:
		offset = XLLP_MICCO_STEREO_AMPLITUDE_CH1;
		if (g_Codec.Reg[offset] &
				 XLLP_MICCO_STEREO_EN){
			g_Codec.Reg[offset] &= 
				~XLLP_MICCO_STEREO_EN;
		}
		break;

	case CODEC_LINE_OUT:
		offset = XLLP_MICCO_AUDIO_LINE_AMP;
		if (g_Codec.Reg[offset] & 
				XLLP_MICCO_AUDIO_LINE_AMP_EN) {
			g_Codec.Reg[offset] &= 
				~XLLP_MICCO_AUDIO_LINE_AMP_EN;
		}
		break;

	case CODEC_ADC:
		offset = XLLP_MICCO_VCODEC_ADC_CONTROL;
		if (g_Codec.Reg[offset] & 
				XLLP_MICCO_VCODEC_ADC_ON_EN) {
			g_Codec.Reg[offset] &= 
				~XLLP_MICCO_VCODEC_ADC_ON_EN;
		}
		break;
	default:
		return XLLP_ACODEC_GENERAL_SW_ERR;
	}
       MICCO_WRITE(offset);
	return XLLP_ACODEC_SUCCESS;
}

XLLP_ACODEC_ERROR_T MiccoCodecSetOutputVolume(enum MiccoOutput out, int volume)
{
       XLLP_UINT8_T offset;
	switch(out){
	case CODEC_BEAR:
		offset = XLLP_MICCO_BEAR_VOL;
		g_Codec.Reg[offset] = (~0x3F & 
			g_Codec.Reg[offset]) | volume;
		break;

	case CODEC_MONO:
		offset = XLLP_MICCO_MONO_VOL;
		g_Codec.Reg[offset] = (~0x3F &
			g_Codec.Reg[offset]) | volume;
		break;

	case CODEC_STEREO:
		offset = XLLP_MICCO_STEREO_AMPLITUDE_CH1;
		g_Codec.Reg[offset] =
			(~0x3F & g_Codec.Reg[offset]) |	volume;
		break;

	case CODEC_LINE_OUT:
		offset = XLLP_MICCO_AUDIO_LINE_AMP;
		g_Codec.Reg[offset] =
			(~0x0F & g_Codec.Reg[offset]) |	volume;
		break;

	default:
		return XLLP_ACODEC_GENERAL_SW_ERR;
	}
       MICCO_WRITE(offset);
	return XLLP_ACODEC_SUCCESS;
}

XLLP_ACODEC_ERROR_T MiccoCodecEnableInput(enum MiccoInput in)
{
    XLLP_UINT8_T	Data;
    XLLP_UINT8_T reg;
    XLLP_UINT8_T offset;

	switch(in){
	case CODEC_AUX1:
		offset = XLLP_MICCO_PGA_AUX1_2;
		if (!(g_Codec.Reg[offset] & XLLP_MICCO_PGA_AUX1_EN)) {
			g_Codec.Reg[offset] |= XLLP_MICCO_PGA_AUX1_EN;
			MICCO_WRITE(offset);
		}
		break;

	case CODEC_AUX2:
		offset = XLLP_MICCO_PGA_AUX1_2;
		if (!(g_Codec.Reg[XLLP_MICCO_PGA_AUX1_2] & XLLP_MICCO_PGA_AUX2_EN)) {
			g_Codec.Reg[XLLP_MICCO_PGA_AUX1_2] |= XLLP_MICCO_PGA_AUX2_EN;
			MICCO_WRITE(XLLP_MICCO_PGA_AUX1_2);
		}
		break;

	case CODEC_AUX3:
		offset = XLLP_MICCO_PGA_AUX3;
		if (!(g_Codec.Reg[XLLP_MICCO_PGA_AUX3] & XLLP_MICCO_PGA_AUX3_EN)) {
			g_Codec.Reg[XLLP_MICCO_PGA_AUX3] |= XLLP_MICCO_PGA_AUX3_EN;
			MICCO_WRITE(XLLP_MICCO_PGA_AUX3);
		}
		break;

	case CODEC_MIC1:
		offset = XLLP_MICCO_MIC_PGA;
		g_Codec.Reg[XLLP_MICCO_MIC_PGA] &= ~XLLP_MICCO_MIC_PGA_SELMIC_2;
		g_Codec.Reg[XLLP_MICCO_MIC_PGA] =
		       g_Codec.Reg[XLLP_MICCO_MIC_PGA] |
		       XLLP_MICCO_MIC_PGA_EXT_EN | XLLP_MICCO_MIC_PGA_INT_EN |
		       XLLP_MICCO_MIC_PGA_AMP_EN;
		MICCO_WRITE(XLLP_MICCO_MIC_PGA);
		break;

	case CODEC_MIC2:
		offset = XLLP_MICCO_MIC_PGA;
		g_Codec.Reg[XLLP_MICCO_MIC_PGA] |= XLLP_MICCO_MIC_PGA_SELMIC_2;
		g_Codec.Reg[XLLP_MICCO_MIC_PGA] =
		       g_Codec.Reg[XLLP_MICCO_MIC_PGA] |
		       XLLP_MICCO_MIC_PGA_EXT_EN | XLLP_MICCO_MIC_PGA_INT_EN |
		       XLLP_MICCO_MIC_PGA_AMP_EN;
		MICCO_WRITE(XLLP_MICCO_MIC_PGA);
		break;

	case CODEC_PCM:

		if (!(g_Codec.Reg[XLLP_MICCO_VCODEC_VDAC_CONTROL] &
				XLLP_MICCO_VDAC_ON)) {
			g_Codec.Reg[XLLP_MICCO_VCODEC_VDAC_CONTROL] |=
				XLLP_MICCO_VDAC_ON;
			g_Codec.Reg[XLLP_MICCO_VCODEC_VDAC_CONTROL] &=
				~XLLP_MICCO_VDAC_HPF_MUTE;
			MICCO_WRITE(XLLP_MICCO_VCODEC_VDAC_CONTROL);
		}
/* fix the noise in PCM record */		
        reg = XLLP_MICCO_BUCK_SLEEP;
        Data = 0xFF;
        XllpPmMiccoRead((XLLP_I2C_T*)g_Codec.I2CBase, (XLLP_OST_T*)g_Codec.OSTBase,
		    reg, &Data);
        Data |= (1 << 5);
        XllpPmMiccoWrite((XLLP_I2C_T*)g_Codec.I2CBase, (XLLP_OST_T*)g_Codec.OSTBase,
		    reg, Data);
		break;
	
	case CODEC_HIFI:
              NKDbgPrintfW(TEXT("in set CODEC HIFI Enable Input \r\n")); 

              offset = XLLP_MICCO_MUX_MONO;
              g_Codec.Reg[offset] = 0x08; /* MONO */
              MICCO_WRITE(offset);	

              offset = XLLP_MICCO_STEREO_AMPLITUDE_CH1;
              g_Codec.Reg[offset] = 0xC0; /* STEREO 1 */
              MICCO_WRITE(offset);	

              offset = XLLP_MICCO_STEREO_AMPLITUDE_CH2;
              g_Codec.Reg[offset] = 0x0; /* STEREO 1 */
              MICCO_WRITE(offset);	

              offset = XLLP_MICCO_MONO_VOL;
              g_Codec.Reg[offset] = 0xC0; 
              MICCO_WRITE(offset);	

              offset = XLLP_MICCO_BEAR_VOL;
              g_Codec.Reg[offset] = 0xC0; 
              MICCO_WRITE(offset);	

		offset = XLLP_MICCO_HIFI_DAC_CONTROL;
		if (!(g_Codec.Reg[XLLP_MICCO_HIFI_DAC_CONTROL] &
				XLLP_MICCO_HIFI_DAC_ON)) {
			g_Codec.Reg[XLLP_MICCO_HIFI_DAC_CONTROL] |=
				XLLP_MICCO_HIFI_DAC_ON;
			MICCO_WRITE(XLLP_MICCO_HIFI_DAC_CONTROL);
		}
		break;

	default:
		return XLLP_ACODEC_GENERAL_SW_ERR;
	}
	return XLLP_ACODEC_SUCCESS;
}

XLLP_ACODEC_ERROR_T MiccoCodecDisableInput(enum MiccoInput in)
{
    XLLP_UINT8_T	Data;
    XLLP_UINT8_T reg;			  

	switch(in){
	case CODEC_AUX1:
		if (g_Codec.Reg[XLLP_MICCO_PGA_AUX1_2] & XLLP_MICCO_PGA_AUX1_EN) {
			g_Codec.Reg[XLLP_MICCO_PGA_AUX1_2] &=
				~XLLP_MICCO_PGA_AUX1_EN;
			MICCO_WRITE(XLLP_MICCO_PGA_AUX1_2);
		}
		break;

	case CODEC_AUX2:
		if (g_Codec.Reg[XLLP_MICCO_PGA_AUX1_2] & XLLP_MICCO_PGA_AUX2_EN) {
			g_Codec.Reg[XLLP_MICCO_PGA_AUX1_2] &=
				~XLLP_MICCO_PGA_AUX2_EN;
			MICCO_WRITE(XLLP_MICCO_PGA_AUX1_2);
		}
		break;

	case CODEC_AUX3:
		if (g_Codec.Reg[XLLP_MICCO_PGA_AUX3] & XLLP_MICCO_PGA_AUX3_EN) {
			g_Codec.Reg[XLLP_MICCO_PGA_AUX3] &= ~XLLP_MICCO_PGA_AUX3_EN;
			MICCO_WRITE(XLLP_MICCO_PGA_AUX3);
		}
		break;

	case CODEC_MIC1:
		g_Codec.Reg[XLLP_MICCO_MIC_PGA] =
			g_Codec.Reg[XLLP_MICCO_MIC_PGA] & 
			~(XLLP_MICCO_MIC_PGA_EXT_EN | XLLP_MICCO_MIC_PGA_INT_EN |
				XLLP_MICCO_MIC_PGA_AMP_EN);
		MICCO_WRITE(XLLP_MICCO_MIC_PGA);
		break;

	case CODEC_MIC2:
		g_Codec.Reg[XLLP_MICCO_MIC_PGA] =
			g_Codec.Reg[XLLP_MICCO_MIC_PGA] & 
			~(XLLP_MICCO_MIC_PGA_EXT_EN | XLLP_MICCO_MIC_PGA_INT_EN |
				XLLP_MICCO_MIC_PGA_AMP_EN);
		MICCO_WRITE(XLLP_MICCO_MIC_PGA);
		break;

	case CODEC_PCM:
		if (g_Codec.Reg[XLLP_MICCO_VCODEC_VDAC_CONTROL] &
				XLLP_MICCO_VDAC_ON) {
			g_Codec.Reg[XLLP_MICCO_VCODEC_VDAC_CONTROL]  &=
				~XLLP_MICCO_VDAC_ON;

		}

/* fix the noise in PCM record */		
        reg = XLLP_MICCO_BUCK_SLEEP;
        Data = 0xFF;
        XllpPmMiccoRead((XLLP_I2C_T*)g_Codec.I2CBase, (XLLP_OST_T*)g_Codec.OSTBase,
		    reg, &Data);
        Data &= ~(1 << 5);
        XllpPmMiccoWrite((XLLP_I2C_T*)g_Codec.I2CBase, (XLLP_OST_T*)g_Codec.OSTBase,
		    reg, Data);


		break;
	
	case CODEC_HIFI:
		if (g_Codec.Reg[XLLP_MICCO_HIFI_DAC_CONTROL] &
				XLLP_MICCO_HIFI_DAC_ON) {
			g_Codec.Reg[XLLP_MICCO_HIFI_DAC_CONTROL] &=
				~XLLP_MICCO_HIFI_DAC_ON;
			MICCO_WRITE(XLLP_MICCO_HIFI_DAC_CONTROL);
		}
		break;

	default:
		return XLLP_ACODEC_GENERAL_SW_ERR;
	}
	return XLLP_ACODEC_SUCCESS;
}

XLLP_ACODEC_ERROR_T MiccoCodecSetInputGain(enum MiccoInput in, int volume)
{
       XLLP_UINT8_T offset = 0;
	switch(in){
	case CODEC_AUX1:
		offset = XLLP_MICCO_PGA_AUX1_2;
		g_Codec.Reg[XLLP_MICCO_PGA_AUX1_2] = ((~0x03 & 
			g_Codec.Reg[XLLP_MICCO_PGA_AUX1_2]) | volume);
		MICCO_WRITE(XLLP_MICCO_PGA_AUX1_2);
		break;

	case CODEC_AUX2:
		offset = XLLP_MICCO_PGA_AUX1_2;
		g_Codec.Reg[XLLP_MICCO_PGA_AUX1_2] = ((~0x30 & 
			g_Codec.Reg[XLLP_MICCO_PGA_AUX1_2]) | (volume<< 4));
		MICCO_WRITE(XLLP_MICCO_PGA_AUX1_2);
		break;
	
	case CODEC_AUX3:
		offset = XLLP_MICCO_PGA_AUX3;
		g_Codec.Reg[XLLP_MICCO_PGA_AUX3] = ((~0x03 & 
			g_Codec.Reg[XLLP_MICCO_PGA_AUX3]) | volume);
		MICCO_WRITE(XLLP_MICCO_PGA_AUX3);
		break;

	case CODEC_MIC1:
	case CODEC_MIC2:
		offset = XLLP_MICCO_MIC_PGA;
		g_Codec.Reg[XLLP_MICCO_MIC_PGA] = ((~0x07 &
			g_Codec.Reg[XLLP_MICCO_MIC_PGA]) | volume);
		MICCO_WRITE(XLLP_MICCO_MIC_PGA);
		break;
	
	case CODEC_PCM:	/* Need check whether HIFI and PCM support input? */
	case CODEC_HIFI:
		break;

	default:
		return XLLP_ACODEC_GENERAL_SW_ERR;
	}
	return XLLP_ACODEC_SUCCESS;
}

XLLP_ACODEC_ERROR_T MiccoCodecSetSampleRate(enum MiccoCodecDigitalPort Port, int Rate)
{
	switch(Port){
	case MICCO_VOICE:
		switch (Rate) {
		case 8000:
			g_Codec.Reg[XLLP_MICCO_VCODEC_ADC_CONTROL] = 
				(~(0x03 << 3) & 
				 g_Codec.Reg[XLLP_MICCO_VCODEC_ADC_CONTROL]);
			break;

		case 16000:
			//16khz pcm sample rate
			g_Codec.Reg[XLLP_MICCO_VCODEC_ADC_CONTROL] =
				(~(0x03 << 3) &
				 g_Codec.Reg[XLLP_MICCO_VCODEC_ADC_CONTROL]) |
				(0x01 << 3);

			break;

		case 32000:
			g_Codec.Reg[XLLP_MICCO_VCODEC_ADC_CONTROL] |=(0x3 << 3);
			break;
		default:
			return XLLP_ACODEC_SAMPLERATE_NOT_SUPPORTED;
		}
		MICCO_WRITE(XLLP_MICCO_VCODEC_ADC_CONTROL);
		break;

	case MICCO_HIFI:
		switch (Rate) {
		case 8000:
			g_Codec.Reg[XLLP_MICCO_I2S_CONTROL] &= 0xF0;
			break;
		case 11025:
			g_Codec.Reg[XLLP_MICCO_I2S_CONTROL] &= 0xF0;
			g_Codec.Reg[XLLP_MICCO_I2S_CONTROL] |= 0x01;
			break;
		case 12000:
			g_Codec.Reg[XLLP_MICCO_I2S_CONTROL] &= 0xF0;
			g_Codec.Reg[XLLP_MICCO_I2S_CONTROL] |= 0x02;
			break;
		case 16000:
			g_Codec.Reg[XLLP_MICCO_I2S_CONTROL] &= 0xF0;
			g_Codec.Reg[XLLP_MICCO_I2S_CONTROL] |= 0x03;
			break;
		case 22050:
			g_Codec.Reg[XLLP_MICCO_I2S_CONTROL] &= 0xF0;
			g_Codec.Reg[XLLP_MICCO_I2S_CONTROL] |= 0x04;
			break;
		case 24000:
			g_Codec.Reg[XLLP_MICCO_I2S_CONTROL] &= 0xF0;
			g_Codec.Reg[XLLP_MICCO_I2S_CONTROL] |= 0x05;
			break;
		case 32000:
			g_Codec.Reg[XLLP_MICCO_I2S_CONTROL] &= 0xF0;
			g_Codec.Reg[XLLP_MICCO_I2S_CONTROL] |= 0x06;
			break;
		case 44100:
			g_Codec.Reg[XLLP_MICCO_I2S_CONTROL] &= 0xF0;
			g_Codec.Reg[XLLP_MICCO_I2S_CONTROL] |= 0x07;
			break;
		case 48000:
			g_Codec.Reg[XLLP_MICCO_I2S_CONTROL] &= 0xF0;
			g_Codec.Reg[XLLP_MICCO_I2S_CONTROL] |= 0x0F;
			break;
		default:
			return XLLP_ACODEC_SAMPLERATE_NOT_SUPPORTED;
		}
		MICCO_WRITE(XLLP_MICCO_I2S_CONTROL);
		break;
	default:
		return XLLP_ACODEC_SAMPLERATE_NOT_SUPPORTED;
	}
	return XLLP_ACODEC_SUCCESS;
}

/////////////////////////////////////////////////////////////////////////
// XLLP Functions
/////////////////////////////////////////////////////////////////////////
XLLP_ACODEC_ERROR_T MCWrite(XLLP_ACODEC_CONTEXT_T* pContext, 
				XLLP_UINT16_T regoffset, 
				XLLP_UINT16_T pRegvalue)
{
	XLLP_UINT16_T	Offset;
	Offset = regoffset-XLLP_MICCO_AUDIO_REG_BASE;
	g_Codec.Reg[Offset] = (XLLP_UINT8_T)pRegvalue;
	MICCO_WRITE((XLLP_UINT8_T)Offset);
	return XLLP_ACODEC_SUCCESS;
}

XLLP_ACODEC_ERROR_T MCRead(XLLP_ACODEC_CONTEXT_T *pContext,
				XLLP_UINT16_T regoffset, 
				XLLP_UINT16_T *pRegvalue)
{
	*pRegvalue = g_Codec.Reg[regoffset-XLLP_MICCO_AUDIO_REG_BASE];
	return XLLP_ACODEC_SUCCESS;
}

XLLP_ACODEC_ERROR_T MCSetMasterVol(XLLP_ACODEC_CONTEXT_T* pContext,
				XLLP_UINT16_T GainInDb)
{

	// TODO
	// What's the maximum and minimum gain?
	// How to map it to a negative Db value?
#if 0
	MiccoCodecSetOutputVolume(CODEC_BEAR,GainInDb);
	MiccoCodecSetOutputVolume(CODEC_MONO,GainInDb);
	MiccoCodecSetOutputVolume(CODEC_STEREO,GainInDb);
	MiccoCodecSetOutputVolume(CODEC_LINE_OUT,GainInDb);
#endif
	return XLLP_ACODEC_SUCCESS;
}

XLLP_ACODEC_ERROR_T MCSetMasterInputGain(XLLP_ACODEC_CONTEXT_T* pContext,
					XLLP_UINT16_T GainInDb)
{
	// TODO
	// What's the maximum and minimum gain?
	// How to map it to a negative Db value?
#if 0
	MiccoCodecSetInputGain(CODEC_AUX1,GainInDb);
	MiccoCodecSetInputGain(CODEC_AUX2,GainInDb);
	MiccoCodecSetInputGain(CODEC_AUX3,GainInDb);
	MiccoCodecSetInputGain(CODEC_MIC1,GainInDb);
	MiccoCodecSetInputGain(CODEC_MIC2,GainInDb);
#endif
	return XLLP_ACODEC_FEATURE_NO_SUPPORTED;
}

XLLP_ACODEC_ERROR_T MCGetInSampleRate(XLLP_ACODEC_CONTEXT_T *pContext,
					XLLP_UINT16_T * RateInHz)
{
	// FIXME
	// Support only one sample rate in voice path recording
	*RateInHz = PCM_SAMPLERATE;
	return XLLP_ACODEC_SUCCESS;
}

XLLP_ACODEC_ERROR_T MCGetOutSampleRate(XLLP_ACODEC_CONTEXT_T *pContext,
					XLLP_UINT16_T * RateInHz)
{
	*RateInHz = (XLLP_UINT16_T)g_Codec.PlaySampleRate;
	return XLLP_ACODEC_SUCCESS;
}

XLLP_ACODEC_ERROR_T MCSetInSampleRate(XLLP_ACODEC_CONTEXT_T *pContext,
					XLLP_UINT16_T RateInHz)
{
	if(RateInHz==PCM_SAMPLERATE){
		MiccoCodecSetSampleRate(MICCO_VOICE,RateInHz);
		return XLLP_ACODEC_SUCCESS;
	}
	else
		return XLLP_ACODEC_SAMPLERATE_NOT_SUPPORTED;
}

XLLP_ACODEC_ERROR_T MCSetOutSampleRate(XLLP_ACODEC_CONTEXT_T *pContext,
					XLLP_UINT16_T RateInHz)
{
	XLLP_SSP_PORT_SETUP_T	Setup;
	XLLP_SSP_OP_STATE_T	opState;
	Setup.portClock.clockRate = RateInHz;

	NKDbgPrintfW(TEXT("[WAVEDEV2] MCSetOutSampleRate! \r\n"));
#ifdef	MICCO_MASTER_MODE
	opState = XLLP_SSP_MASTER;
#else
	opState = XLLP_SSP_SLAVE;
#endif
    Setup.portClock.clockRate = RateInHz;


	if(XLLP_STATUS_SUCCESS!=XllpSSPPortConfigure(XLLP_SSP_MICCO_I2S,
						&Setup,
						opState,
						(XLLP_SSP_REGISTER_T*)g_Codec.I2SBase))
	{
		return XLLP_ACODEC_SAMPLERATE_NOT_SUPPORTED;
	}
	g_Codec.PlaySampleRate = RateInHz;
	return MiccoCodecSetSampleRate(MICCO_HIFI,RateInHz);
}
XLLP_ACODEC_ERROR_T MCFMPathEnable(XLLP_ACODEC_CONTEXT_T *pContext)
{
	return MiccoCodecEnableInput(CODEC_AUX2);
}
XLLP_ACODEC_ERROR_T MCFMPathDisable(XLLP_ACODEC_CONTEXT_T *pContext)
{
	return MiccoCodecDisableInput(CODEC_AUX2);
}

XLLP_ACODEC_ERROR_T MCModemPathEnable(XLLP_ACODEC_CONTEXT_T *pContext)
{
	return MiccoCodecEnableInput(CODEC_AUX1);
}

XLLP_ACODEC_ERROR_T MCModemPathDisable(XLLP_ACODEC_CONTEXT_T *pContext)
{
	return MiccoCodecDisableInput(CODEC_AUX1);
}

XLLP_ACODEC_ERROR_T MCHiFiPathEnable(XLLP_ACODEC_CONTEXT_T *pContext)
{
	return MiccoCodecEnableInput(CODEC_HIFI);
}

XLLP_ACODEC_ERROR_T MCHiFiPathDisable(XLLP_ACODEC_CONTEXT_T *pContext)
{
	return MiccoCodecDisableInput(CODEC_HIFI);
}

XLLP_ACODEC_ERROR_T MCRecordingPathEnable(XLLP_ACODEC_CONTEXT_T *pContext)
{
	// Enable voice path for near end recording
	return MiccoCodecEnableInput(CODEC_PCM);
}

XLLP_ACODEC_ERROR_T MCRecordingPathDisable(XLLP_ACODEC_CONTEXT_T *pContext)
{
	// Disable voice path for near end recording
	return MiccoCodecDisableInput(CODEC_PCM);
}

XLLP_ACODEC_ERROR_T MCVoicePathEnable(XLLP_ACODEC_CONTEXT_T *pContext)
{
	// TODO
	// Set sample rate to 16000 for debugging
	MiccoCodecSetSampleRate(MICCO_VOICE, PCM_SAMPLERATE);
	return MiccoCodecEnableInput(CODEC_PCM);
}

XLLP_ACODEC_ERROR_T MCVoicePathDisable(XLLP_ACODEC_CONTEXT_T *pContext)
{
	return MiccoCodecDisableInput(CODEC_PCM);
}

XLLP_ACODEC_ERROR_T MCSideToneEnable(XLLP_ACODEC_CONTEXT_T *pContext)
{
	g_Codec.Reg[XLLP_MICCO_SIDETONE] |= 0x80;
	MICCO_WRITE(XLLP_MICCO_SIDETONE);
	return XLLP_ACODEC_SUCCESS;
}

XLLP_ACODEC_ERROR_T MCSideToneDisable(XLLP_ACODEC_CONTEXT_T *pContext)
{
	g_Codec.Reg[XLLP_MICCO_SIDETONE] &= 0x7F;
	MICCO_WRITE(XLLP_MICCO_SIDETONE);
	return XLLP_ACODEC_SUCCESS;
}

XLLP_ACODEC_ERROR_T MCHeadsetEnable(XLLP_ACODEC_CONTEXT_T *pContext)
{
	return MiccoCodecEnableOutput(CODEC_STEREO);
/*    XLLP_UINT8_T offset;
    XLLP_UINT8_T   value;	

    for (offset = 0; offset < XLLP_MICCO_AUDIO_REGS_NUM; offset ++)
        g_Codec_saved.Reg[offset] = g_Codec.Reg[offset];

    offset = XLLP_MICCO_MUX_MONO;
    g_Codec.Reg[offset] = 0x1<<5;
    MICCO_WRITE(offset);

    offset = XLLP_MICCO_MUX_BEAR;
    g_Codec.Reg[offset] = 0x1<<5;
    MICCO_WRITE(offset);

    offset = XLLP_MICCO_MUX_STEREO_CH1;
    g_Codec.Reg[offset] = 0x1<<5;	

    offset = XLLP_MICCO_MUX_STEREO_CH2;
    g_Codec.Reg[offset] = 0x1<<5;	

    offset = XLLP_MICCO_MUX_LINE_OUT;
    g_Codec.Reg[offset] = 0x1<<7;

    offset = XLLP_MICCO_AUDIO_LINE_AMP;
    g_Codec.Reg[offset] = 0x13; 

    offset = XLLP_MICCO_STEREO_AMPLITUDE_CH1;
    g_Codec.Reg[offset] = 0xC0; 
    MICCO_WRITE(offset);

    offset = XLLP_MICCO_STEREO_AMPLITUDE_CH2;
    g_Codec.Reg[offset] = 0x3f; 
    MICCO_WRITE(offset);

    offset = XLLP_MICCO_HIFI_DAC_CONTROL; 
    g_Codec.Reg[offset] = XLLP_MICCO_HIFI_DAC_ON;

    offset = XLLP_MICCO_MONO_VOL;
    g_Codec.Reg[offset] = 0xf; 
    MICCO_WRITE(offset);

    offset = XLLP_MICCO_BEAR_VOL;
    g_Codec.Reg[offset] = 0x40; 
    MICCO_WRITE(offset);

     //Micco as I2S slave. Use I2S MSB normal mode 
    offset = XLLP_MICCO_I2S_CONTROL;
    g_Codec.Reg[offset] = 0xDF;

    offset = XLLP_MICCO_TX_PGA;
    g_Codec.Reg[offset] = 0x08;	

    offset = XLLP_MICCO_MIC_PGA;
	g_Codec.Reg[offset] = XLLP_MICCO_MIC_PGA_EXT_EN | XLLP_MICCO_MIC_PGA_INT_EN |
	//        XLLP_MICCO_MIC_PGA_SELMIC_2 | XLLP_MICCO_MIC_PGA_AMP_EN |
	    XLLP_MICCO_MIC_PGA_AMP_EN |
	    0x7;		
    g_Codec.Reg[offset] = 0x2f;	

    offset = XLLP_MICCO_TX_PGA_MUX;
    g_Codec.Reg[offset] = 0x0c;

    offset = XLLP_MICCO_VCODEC_ADC_CONTROL;
    //g_Codec.Reg[offset] = XLLP_MICCO_VCODEC_ADC_ON_EN;
    g_Codec.Reg[offset] = 0x2d;	// 24 dB 
    
    offset = XLLP_MICCO_MIC_PGA;
    g_Codec.Reg[offset] = XLLP_MICCO_MIC_PGA_EXT_EN | XLLP_MICCO_MIC_PGA_INT_EN |
//        XLLP_MICCO_MIC_PGA_SELMIC_2 | XLLP_MICCO_MIC_PGA_AMP_EN |
        XLLP_MICCO_MIC_PGA_AMP_EN |
        0x7;	// 30 dB	

    offset = XLLP_MICCO_TX_PGA_MUX;
    g_Codec.Reg[offset] = 0x0c;

    offset = XLLP_MICCO_VCODEC_ADC_CONTROL;
    g_Codec.Reg[offset] = 0x6d;

    // PCM_SDI normal operation, PCM_SDO enabled 
    offset = XLLP_MICCO_VCODEC_VDAC_CONTROL;
//    g_Codec.Reg[offset] = XLLP_MICCO_VDAC_ON | XLLP_MICCO_VDAC_HPF_BYPASS;
    g_Codec.Reg[offset] = XLLP_MICCO_VDAC_HPF_BYPASS;

    offset = XLLP_MICCO_SIDETONE;
    g_Codec.Reg[offset] = 0; 

    // Enable AUX1,2. AUX1, 2 gain 0dB 
    offset = XLLP_MICCO_PGA_AUX1_2;
    g_Codec.Reg[offset] = 0;

    // Enable AUX3. AUX3 gain 0 dB 	
    offset = XLLP_MICCO_PGA_AUX3;
    g_Codec.Reg[offset] = 0;

    // DAC1, 2, 3 gain 0dB 
    offset = XLLP_MICCO_PGA_DACS;
    g_Codec.Reg[offset] = 0x00;

    //Soft start for MONO, BEAR LINE and STEREO is 61.5ms 
    offset = XLLP_MICCO_SOFT_START_RAMP;
    g_Codec.Reg[offset] = 0xff;

    for (offset = 0; offset < XLLP_MICCO_AUDIO_REGS_NUM; offset++)
    {
        MICCO_WRITE(offset);    
    }

    //enable headset
    offset = XLLP_MICCO_STEREO_AMPLITUDE_CH1;
    g_Codec.Reg[offset] |= XLLP_MICCO_STEREO_EN;
    MICCO_WRITE(offset);

    //disable loudspeaker
    offset = XLLP_MICCO_MONO_VOL;
    g_Codec.Reg[offset] &= ~XLLP_MICCO_MONO_EN;
    MICCO_WRITE(offset);

    return XLLP_ACODEC_SUCCESS;*/

}

XLLP_ACODEC_ERROR_T MCHeadsetDisable(XLLP_ACODEC_CONTEXT_T *pContext)
{
	return MiccoCodecDisableOutput(CODEC_STEREO);
    /*XLLP_UINT8_T offset;

    for (offset = 0; offset < XLLP_MICCO_AUDIO_REGS_NUM; offset ++)
        g_Codec.Reg[offset] = g_Codec_saved.Reg[offset];

    // Restore all Audio register value to Codec
    for (offset = 0; offset < XLLP_MICCO_AUDIO_REGS_NUM; offset ++)
	MICCO_WRITE(offset);	

    return XLLP_ACODEC_SUCCESS;*/
}

XLLP_ACODEC_ERROR_T MCEarSpeakerEnable(XLLP_ACODEC_CONTEXT_T *pContext)
{
	return MiccoCodecEnableOutput(CODEC_BEAR);
}

XLLP_ACODEC_ERROR_T MCEarSpeakerDisable(XLLP_ACODEC_CONTEXT_T *pContext)
{
	return MiccoCodecDisableOutput(CODEC_BEAR);
}

XLLP_ACODEC_ERROR_T MCLoudSpeakerEnable(XLLP_ACODEC_CONTEXT_T *pContext)
{
	return MiccoCodecEnableOutput(CODEC_STEREO);
}

XLLP_ACODEC_ERROR_T MCLoudSpeakerDisable(XLLP_ACODEC_CONTEXT_T *pContext)
{
	return MiccoCodecDisableOutput(CODEC_STEREO);
}
XLLP_ACODEC_ERROR_T MCLineOutEnable(XLLP_ACODEC_CONTEXT_T *pContext)
{
	return MiccoCodecEnableOutput(CODEC_LINE_OUT);
}
XLLP_ACODEC_ERROR_T MCLineOutDisable(XLLP_ACODEC_CONTEXT_T *pContext)
{
	return MiccoCodecDisableOutput(CODEC_LINE_OUT);
}
XLLP_ACODEC_ERROR_T MCVoiceOutEnable(XLLP_ACODEC_CONTEXT_T *pContext)
{
	return MiccoCodecEnableOutput(CODEC_ADC);
}
XLLP_ACODEC_ERROR_T MCVoiceOutDisable(XLLP_ACODEC_CONTEXT_T *pContext)
{
	return MiccoCodecDisableOutput(CODEC_ADC);
}

XLLP_ACODEC_ERROR_T MCMicEnable(XLLP_ACODEC_CONTEXT_T *pContext,
				XLLP_UINT8_T micType)
{
	enum MiccoInput Port;
	if(micType)
		Port = CODEC_MIC2;
	else
		Port = CODEC_MIC1;
	return MiccoCodecEnableInput(Port);
}

XLLP_ACODEC_ERROR_T MCMicDisable(XLLP_ACODEC_CONTEXT_T *pContext,
				XLLP_UINT8_T micType)
{
	enum MiccoInput Port;
	if(micType)
		Port = CODEC_MIC2;
	else
		Port = CODEC_MIC1;
	return MiccoCodecDisableInput(Port);
}

/*XLLP_ACODEC_ERROR_T MCGetVoiceOutSampleRate(XLLP_ACODEC_CONTEXT_T *pContext,
					XLLP_UINT16_T *RateInHz)
{
	if(g_Codec.Reg[MICCO_VOICE_ADC_CTL]&0x8)
		*RateInHz = 16000;
	else
		*RateInHz = 8000;
	return XLLP_ACODEC_SUCCESS;
}

XLLP_ACODEC_ERROR_T MCSetVoiceOutSampleRate(XLLP_ACODEC_CONTEXT_T *pContext,
					XLLP_UINT16_T RateInHz)
{
	MiccoCodecSetSampleRate(MICCO_VOICE,RateInHz);
}

XLLP_ACODEC_ERROR_T MCSleepCodec(XLLP_ACODEC_CONTEXT_T *pContext)
{
	return XLLP_ACODEC_FEATURE_NO_SUPPORTED;
}

XLLP_ACODEC_ERROR_T MCWakeCodec(XLLP_ACODEC_CONTEXT_T *pContext)
{
	return XLLP_ACODEC_FEATURE_NO_SUPPORTED;
}*/
/////////////////////////////////////////////////////////////////////////
// Route Control Interface
/////////////////////////////////////////////////////////////////////////
// Input Port Map
// INDEX_HIFI_NEAR_IN_CONTROL -> I2S (DAC1+DAC2)
// INDEX_VOICE_NEAR_IN_CONTROL -> PCM DAC3
// INDEX_MIC1_IN_CONTROL -> Mic1
// INDEX_MIC2_IN_CONTROL -> Mic2
// INDEX_FM_IN_CONTROL -> Aux1+Aux2 (Depends on Hardware)
// Output Port Map
// INDEX_STEREO_HEAD_SET_OUT_CONTROL -> Stereo PA
// INDEX_SPEAKER_OUT_CONTRO -> Mono PA
// INDEX_SPEAKER_OUT_CONTRO -> Bear PA
// INDEX_SPEAKER_OUT_CONTRO -> Line Out
// INDEX_VOICE_NEAR_OUT_CONTROL -> Voice ADC
XLLP_ACODEC_ERROR_T MCCheckRoute(XLLP_ACODEC_CONTEXT_T* pContext,
				XLLP_UINT16_T * RouteMap)
{
	// Rule 1
	// DAC3 cannot be connected to TX PGA
	if(RouteMap[XLLP_INDEX_BT_VOICE_NEAR_IN_CONTROL] & 
		XLLP_INDEX_VOICE_NEAR_OUT_CONTROL){
		return XLLP_ACODEC_FEATURE_NO_SUPPORTED;
	}
	
	// Rule 2
	// Mic1 and Mic2 cannot be enabled at the same time
	if(RouteMap[XLLP_INDEX_MIC1_IN_CONTROL]&&RouteMap[XLLP_INDEX_MIC2_IN_CONTROL])
		return XLLP_ACODEC_FEATURE_NO_SUPPORTED;

	return XLLP_ACODEC_SUCCESS;
}

XLLP_ACODEC_ERROR_T MCGetVol(struct _CONTEXT_T *pDeviceContext,
			VOL_PORT_TYPE_T port,
			XLLP_UINT16_T *GainInDb)
{
	return XLLP_ACODEC_SUCCESS;
}

XLLP_ACODEC_ERROR_T MCSetVol(struct _CONTEXT_T *pDeviceContext,
			VOL_PORT_TYPE_T port,
			XLLP_UINT16_T GainInDb)
{
     	NKDbgPrintfW(TEXT("[Codec] MCSetVol \r\n"));
	return XLLP_ACODEC_SUCCESS;
}

XLLP_ACODEC_ERROR_T MCSetRoute(XLLP_ACODEC_CONTEXT_T* pContext,
				XLLP_UINT16_T * RouteMap,
				XLLP_UINT16_T*	CurrentMap)
{
	XLLP_ACODEC_ERROR_T	Result;
	int	TxPGACount,MonoCount,StereoCount, BearCount;
	int	MicCount;

     	NKDbgPrintfW(TEXT("[Codec] MCSetRoute \r\n"));

	Result = MCCheckRoute(pContext,RouteMap);
	if(Result!=XLLP_ACODEC_SUCCESS)return Result;
	TxPGACount = 0;
	MonoCount = 0;
	StereoCount = 0;
	BearCount = 0;
	MicCount = 0;
#if 1
#define XLLP_MICCO_MUX_MONO 		0x0
#define XLLP_MICCO_MUX_BEAR 		0x1
#define XLLP_MICCO_MUX_LINE_OUT 	0x2
	
#define XLLP_MICCO_MUX_STEREO_CH1		0x3
	
#define XLLP_MICCO_MUX_STEREO_CH2		0x4

	// INDEX_HIFI_NEAR_IN_CONTROL
	if(RouteMap[XLLP_INDEX_HIFI_NEAR_IN_CONTROL] &
		XLLP_STEREO_HEAD_SET_OUT_ROUTE){
		g_Codec.Reg[XLLP_MICCO_MUX_STEREO_CH1]|=0x08;
		g_Codec.Reg[XLLP_MICCO_MUX_STEREO_CH2]|=0x10;
		StereoCount++;
	}
	else{
		g_Codec.Reg[XLLP_MICCO_MUX_STEREO_CH1]&= ~0x08;
		g_Codec.Reg[XLLP_MICCO_MUX_STEREO_CH2]&= ~0x10;
	}
	if(RouteMap[XLLP_INDEX_HIFI_NEAR_IN_CONTROL] &
		XLLP_SPEAKER_OUT_ROUTE){
		g_Codec.Reg[XLLP_MICCO_MUX_MONO]|=0x18;
		MonoCount++;
	}
	else{
		g_Codec.Reg[XLLP_MICCO_MUX_MONO]&=~0x18;
	}
	if(RouteMap[XLLP_INDEX_HIFI_NEAR_IN_CONTROL] & XLLP_VOICE_NEAR_OUT_ROUTE){
		g_Codec.Reg[XLLP_MICCO_TX_PGA_MUX]|=0xC0;
		TxPGACount++;
	}
	else{
		g_Codec.Reg[XLLP_MICCO_TX_PGA_MUX]&=~0xC0;
	}

	// INDEX_VOICE_NEAR_IN_CONTROL
	if(RouteMap[XLLP_INDEX_BT_VOICE_NEAR_IN_CONTROL] &
		XLLP_MODEM_NEAR_OUT_ROUTE){
		g_Codec.Reg[XLLP_MICCO_MUX_LINE_OUT]|=0x20;
		StereoCount++;
	}
	else{
		g_Codec.Reg[XLLP_MICCO_MUX_LINE_OUT]&= ~0x20;
	}
	/*
	if(RouteMap[XLLP_INDEX_VOICE_NEAR_IN_CONTROL] &
		XLLP_SPEAKER_OUT_ROUTE){
		g_Codec.Reg[XLLP_MICCO_RX_AUDIO_MUX2]|=0x80;
		MonoCount++;
	}
	else{
		g_Codec.Reg[XLLP_MICCO_RX_AUDIO_MUX2]&=~0x80;
		TxPGACount++;
	}
*/
	// INDEX_MIC1_IN_CONTROL & INDEX_MIC2_IN_CONTROL 
	if(RouteMap[XLLP_INDEX_MIC1_IN_CONTROL]||RouteMap[XLLP_INDEX_MIC2_IN_CONTROL]){
		// Enable side tone in any case that MIC is enabled. This
		// function has side effect.
		// To playback sound, caller must configure DAC3
		// (INDEX_VOICE_NEAR_IN_CONTROL) properly at the same time.
		g_Codec.Reg[XLLP_MICCO_SIDETONE]|=0x80;
		g_Codec.Reg[XLLP_MICCO_MIC_PGA]|=0x08;
		MicCount++;
		if(RouteMap[XLLP_INDEX_MIC1_IN_CONTROL]){
			g_Codec.Reg[XLLP_MICCO_MIC_PGA] &= ~0x10;
		}
		else{
			g_Codec.Reg[XLLP_MICCO_MIC_PGA] |= 0x10;
		}
		//if((RouteMap[XLLP_INDEX_MIC1_IN_CONTROL]&XLLP_MODEM_NEAR_OUT_ROUTE)||
		//RouteMap[XLLP_INDEX_MIC1_IN_CONTROL]&XLLP_MODEM_NEAR_OUT_ROUTE))
		{
			g_Codec.Reg[XLLP_MICCO_MUX_LINE_OUT] |= 0x80;
		}
		//else
		//{
		//	g_Codec.Reg[XLLP_MICCO_MUX_LINE_OUT] &= ~0x80;
		//}
	}
	else{
		g_Codec.Reg[XLLP_MICCO_SIDETONE]&=~0x80;
		g_Codec.Reg[XLLP_MICCO_MIC_PGA]&=~0x08;
	}

	// INDEX_FM_IN_CONTROL 
    if(RouteMap[XLLP_INDEX_MODEM_IN_CONTROL]&XLLP_STEREO_HEAD_SET_OUT_ROUTE)
    {
		g_Codec.Reg[XLLP_MICCO_MUX_STEREO_CH1]|= 0x01;
		g_Codec.Reg[XLLP_MICCO_MUX_STEREO_CH2]|= 0x02;
		StereoCount++;
    }
	else
	{
		g_Codec.Reg[XLLP_MICCO_MUX_STEREO_CH1]&= ~0x01;
		g_Codec.Reg[XLLP_MICCO_MUX_STEREO_CH2]&= ~0x02;
	}
	if(RouteMap[XLLP_INDEX_MODEM_IN_CONTROL]&XLLP_SPEAKER_OUT_ROUTE)
    {
		g_Codec.Reg[XLLP_MICCO_MUX_MONO] |= 0x03;
		//g_Codec.Reg[XLLP_MICCO_MUX_STEREO_CH2]|= ~0x01;
		MonoCount++;
    }
	else
	{
		g_Codec.Reg[XLLP_MICCO_MUX_MONO] &= ~0x03;
	}
	if(RouteMap[XLLP_INDEX_MODEM_IN_CONTROL]&XLLP_HAND_SET_OUT_ROUTE)
    {
		g_Codec.Reg[XLLP_MICCO_MUX_BEAR] |= 0x03;
		BearCount++;
    }
	else
	{
		g_Codec.Reg[XLLP_MICCO_MUX_BEAR] &= ~0x03;
	}
	if(RouteMap[XLLP_INDEX_MODEM_IN_CONTROL]&XLLP_VOICE_NEAR_OUT_ROUTE)
    {
		g_Codec.Reg[XLLP_MICCO_TX_PGA_MUX]|=0x03;
		TxPGACount++;
    }
	else
	{
		g_Codec.Reg[XLLP_MICCO_TX_PGA_MUX]&=~0x03;
	}
	
	// Write result to register
	MICCO_WRITE(XLLP_MICCO_MUX_STEREO_CH1);
	MICCO_WRITE(XLLP_MICCO_MUX_STEREO_CH2);
	MICCO_WRITE(XLLP_MICCO_MUX_LINE_OUT);
	MICCO_WRITE(XLLP_MICCO_TX_PGA_MUX);
	if(TxPGACount){
		g_Codec.Reg[XLLP_MICCO_TX_PGA] |= 0x20;
	}
	else{
		g_Codec.Reg[XLLP_MICCO_TX_PGA] &= ~0x20;
	}
	MICCO_WRITE(XLLP_MICCO_TX_PGA);
	if(StereoCount){
		g_Codec.Reg[XLLP_MICCO_STEREO_AMPLITUDE_CH1] |= 0x80;
	}
	else{
		g_Codec.Reg[XLLP_MICCO_STEREO_AMPLITUDE_CH1] &= ~0x80;
	}
	MICCO_WRITE(XLLP_MICCO_STEREO_AMPLITUDE_CH1);
	if(MonoCount){
		g_Codec.Reg[XLLP_MICCO_MONO_VOL] |= 0x40;
		
		g_Codec.Reg[XLLP_MICCO_AUDIO_LINE_AMP] |= 0x10;
	}
	else{
		g_Codec.Reg[XLLP_MICCO_MONO_VOL] &= ~0x40;
		
		g_Codec.Reg[XLLP_MICCO_AUDIO_LINE_AMP] &= ~0x10;
	}
	MICCO_WRITE(XLLP_MICCO_MONO_VOL);
	MICCO_WRITE(XLLP_MICCO_AUDIO_LINE_AMP);
	if(BearCount)
	{
		g_Codec.Reg[XLLP_MICCO_BEAR_VOL] |= 0x40;
	}
	else
	{
		g_Codec.Reg[XLLP_MICCO_BEAR_VOL] &= ~0x40;
	}
	MICCO_WRITE(XLLP_MICCO_BEAR_VOL);
	
	if(MicCount){
		MICCO_WRITE(XLLP_MICCO_SIDETONE);
		MICCO_WRITE(XLLP_MICCO_MIC_PGA);
	}
#endif
	return XLLP_ACODEC_SUCCESS;
}

/////////////////////////////////////////////////////////////////////////
// XLLP Interface
/////////////////////////////////////////////////////////////////////////
static const XLLP_UINT32_T voice_mfp_list[]={
	XLLP_MFP_SSP_PCM_CLK_OFFSET,
	XLLP_MFP_SSP_PCM_FRM_OFFSET,
	XLLP_MFP_SSP_PCM_TXD_OFFSET,
	XLLP_MFP_SSP_PCM_RXD_OFFSET,
	XLLP_MFP_PIN_EOLIST_MARKER,
};

static const XLLP_UINT32_T hifi_mfp_list[]={
	XLLP_MFP_SSP_AUDIO_SCLK_OFFSET,
	XLLP_MFP_SSP_AUDIO_FRM_OFFSET,
	XLLP_MFP_SSP_AUDIO_TXD_OFFSET,
//	XLLP_MFP_SSP_AUDIO_RXD_OFFSET,
	XLLP_MFP_PIN_EOLIST_MARKER,
};

static XLLP_MFP_LPM_OUTPUT_T voice_lpm_list[] = {
	XLLP_MFP_LPMO_FLOAT,
	XLLP_MFP_LPMO_FLOAT,
	XLLP_MFP_LPMO_FLOAT,
	XLLP_MFP_LPMO_FLOAT,
};

static XLLP_MFP_LPM_OUTPUT_T hifi_lpm_list[] = {
	XLLP_MFP_LPMO_FLOAT,
	XLLP_MFP_LPMO_FLOAT,
	XLLP_MFP_LPMO_FLOAT,
//	XLLP_MFP_LPMO_FLOAT,
};

XLLP_ACODEC_ERROR_T XllpAcodecInit(XLLP_ACODEC_CONTEXT_T *pContext,
				XLLP_BOOL_T hwInit)
{
	XLLP_STATUS_T	Result;

	// Check init number
	if(pContext->initNumber>0)return XLLP_ACODEC_SUCCESS;
	pContext->initNumber++;

	pContext->g_pfnACodecWrite = MCWrite;
	pContext->g_pfnACodecRead = MCRead;
	pContext->g_pfnSetMasterVol = MCSetMasterVol;
	pContext->g_pfnSetMasterInputGain = MCSetMasterInputGain;
	pContext->g_pfnCodecSpecificInit = NULL;
	pContext->g_pfnGetInSampleRate = MCGetInSampleRate;
	pContext->g_pfnGetOutSampleRate = MCGetOutSampleRate;
	pContext->g_pfnSetInSampleRate = MCSetInSampleRate;
	pContext->g_pfnSetOutSampleRate = MCSetOutSampleRate;
	pContext->g_pfnHiFiStreamPathEnable = MCHiFiPathEnable;
	pContext->g_pfnHiFiStreamPathDisable = MCHiFiPathDisable;
	pContext->g_pfnRecordingPathEnable = MCRecordingPathEnable;
	pContext->g_pfnRecordingPathDisable = MCRecordingPathDisable;
	pContext->g_pfnVoicePathEnable = MCVoicePathEnable;
	pContext->g_pfnVoicePathDisable = MCVoicePathDisable;
	pContext->g_pfnModemPathEnable = MCModemPathEnable;
	pContext->g_pfnModemPathDisable = MCModemPathDisable;
	pContext->g_pfnFMPathEnable = MCFMPathEnable;
	pContext->g_pfnFMPathDisable = MCFMPathDisable;
	pContext->g_pfnSideToneEnable = MCSideToneEnable;
	pContext->g_pfnSideToneDisable = MCSideToneDisable;
	pContext->g_pfnHeadsetEnable = MCHeadsetEnable;
	pContext->g_pfnHeadsetDisable = MCHeadsetDisable;
	pContext->g_pfnEarSpeakerEnable = MCEarSpeakerEnable;
	pContext->g_pfnEarSpeakerDisable = MCEarSpeakerDisable;
	pContext->g_pfnLouderSpeakerEnable = MCLoudSpeakerEnable;
	pContext->g_pfnLouderSpeakerDisable = MCLoudSpeakerDisable;
	pContext->g_pfnLineOutEnable = MCLineOutEnable;
	pContext->g_pfnLineOutDisable = MCLineOutDisable;
	pContext->g_pfnVoiceOutEnable = MCVoiceOutEnable;
	pContext->g_pfnVoiceOutDisable = MCVoiceOutDisable;
	pContext->g_pfnMicEnable = MCMicEnable;
	pContext->g_pfnMicDisable = MCMicDisable;
	pContext->g_pfnGetVolumeTable = NULL;
	pContext->g_pfnSetVolumeTable = NULL;
	pContext->g_pfnGetToneTable = NULL;
	pContext->g_pfnSetToneTable = NULL;
	pContext->g_pfnSetRoute = MCSetRoute;

	g_Codec.I2CBase = (XLLP_UINT32_T)pContext->pCtrlReg;
	g_Codec.OSTBase = (XLLP_UINT32_T)pContext->pOSTRegs;
	g_Codec.MFPBase = (XLLP_UINT32_T)pContext->pMfpRegBase;
	g_Codec.I2SBase = (XLLP_UINT32_T)pContext->pHifiReg;
	g_Codec.SSPBase = (XLLP_UINT32_T)pContext->pVoiceReg;

	// TODO
	// Initialize MFP
	Result = XllpMfpResourceManager_List(pContext->pMfpRmDb,
					(P_XLLP_UINT32_T)hifi_mfp_list,
					XLLP_MFP_RM_ID_XLLP_SSP,
					XLLP_SET);
	if(Result!=XLLP_STATUS_SUCCESS){
		return XLLP_ACODEC_GENERAL_SW_ERR;
	}

	Result = XllpMfpResourceManager_List(pContext->pMfpRmDb,
					(P_XLLP_UINT32_T)voice_mfp_list,
					XLLP_MFP_RM_ID_XLLP_SSP,
					XLLP_SET);
	if(Result!=XLLP_STATUS_SUCCESS){
		return XLLP_ACODEC_GENERAL_SW_ERR;
	}

       // Initialize SSP Port.
       XllpSSPPortInit(pContext);

	// Initialize Micco Audio Registers
	MiccoCodecInit();

	return XLLP_ACODEC_SUCCESS; 
}

XLLP_ACODEC_ERROR_T XllpSSPPortInit(XLLP_ACODEC_CONTEXT_T *pContext)
{
    XLLP_STATUS_T	Result;

    Result =  XllpMfpConfigureLpmOutputLevel_List (
        (P_XLLP_VUINT32_T)pContext->pMfpRegBase,
        (P_XLLP_UINT32_T)voice_mfp_list,
        voice_lpm_list);

    if(Result!=XLLP_STATUS_SUCCESS){
        return XLLP_ACODEC_GENERAL_SW_ERR;
    }

    Result =  XllpMfpConfigureLpmOutputLevel_List (
        (P_XLLP_VUINT32_T)pContext->pMfpRegBase,
        (P_XLLP_UINT32_T)hifi_mfp_list,
        hifi_lpm_list);

    if(Result!=XLLP_STATUS_SUCCESS){
        return XLLP_ACODEC_GENERAL_SW_ERR;
    }

    Result =  XllpMfpActivatePullUpDown_List (
        (P_XLLP_VUINT32_T)pContext->pMfpRegBase,
        (P_XLLP_UINT32_T)voice_mfp_list,
        XLLP_OFF);

    if(Result!=XLLP_STATUS_SUCCESS){
        return XLLP_ACODEC_GENERAL_SW_ERR;
    }

    Result =  XllpMfpActivatePullUpDown_List (
        (P_XLLP_VUINT32_T)pContext->pMfpRegBase,
        (P_XLLP_UINT32_T)hifi_mfp_list,
        XLLP_OFF);

    if(Result!=XLLP_STATUS_SUCCESS){
        return XLLP_ACODEC_GENERAL_SW_ERR;
    }

    XllpSSPMFPConfigure(XLLP_SSP_MICCO_VOICE,
        XLLP_TRUE,
        pContext->pMfpRegBase,
        pContext->pVoiceReg);

    XllpSSPMFPConfigure(XLLP_SSP_MICCO_HIFI,
         XLLP_TRUE,
         pContext->pMfpRegBase,
         pContext->pVoiceReg);

#if 0
        // Initialize SSP3 port for HiFi codec.
        XLLP_SSP_PORT_SETUP_T   Setup;
        XLLP_STATUS_T Status;
        Setup.portClock.clockRate = HIFI_SAMPLERATE;
        Status = XllpSSPPortConfigure(XLLP_SSP_MICCO_I2S,
            &Setup,
            XLLP_SSP_MASTER,
            pContext->m_pVoiceReg);
        if(Status != XLLP_STATUS_SUCCESS)
        {
            NKDbgPrintfW(TEXT("[WAVEDEV2] Configure the SSP3 port in the Audio Codec failed! \r\n"));	
        }
        else
        {
            NKDbgPrintfW(TEXT("[WAVEDEV2] Configure the SSP3 port in the Audio Codec OK! \r\n"));	
        } 
#endif

    return XLLP_ACODEC_SUCCESS; 

}

XLLP_ACODEC_ERROR_T XllpAcodecDeInit(XLLP_ACODEC_CONTEXT_T *pDeviceContext)
{
	return XLLP_ACODEC_SUCCESS; 
}

XLLP_ACODEC_ERROR_T XllpAcodecWakeUp(XLLP_ACODEC_CONTEXT_T *pDeviceContext)
{
    XLLP_UINT8_T offset;

    // Re-initialize SSP Port.
    XllpSSPPortInit(pDeviceContext);

    // Restore all Audio register value to Codec
    for (offset = 0; offset < CODEC_REG_NUM; offset ++)
	MICCO_WRITE(offset);	

    return XLLP_ACODEC_SUCCESS; 
}

XLLP_ACODEC_ERROR_T XllpAcodecSleep(XLLP_ACODEC_CONTEXT_T *pDeviceContext)
{
	return XLLP_ACODEC_SUCCESS; 
}
